home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / search / lsmtool-.6 / lsmtool- / lsmtool-0.6 / lsm.c < prev    next >
C/C++ Source or Header  |  1994-12-27  |  10KB  |  460 lines

  1. /*
  2.  * lsm.c -- routines for manipulating LSM entries and data bases
  3.  * 
  4.  * Lars Wirzenius
  5.  * "@(#)lsmtool:lsm.c,v 1.9 1994/12/27 15:57:14 wirzeniu Exp"
  6.  */
  7.  
  8. #include <assert.h>
  9. #include <ctype.h>
  10. #include <stdarg.h>
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14.  
  15. #include <publib.h>
  16.  
  17. #include "lsm.h"
  18.  
  19.  
  20. static int valid_email(const char *);
  21. static int valid_date(const char *);
  22. static int valid_site(const char *);
  23.  
  24.  
  25. /*
  26.  * Is a field empty?
  27.  */
  28. static int field_is_empty(const char *s) {
  29.     return s != NULL && (s[strspn(s, " \t\n")] == '\0');
  30. }
  31.  
  32.  
  33. /*
  34.  * This array gives the names of the fields.  There is room for user-defined
  35.  * fields.  The array is initialized with those field names whose order
  36.  * is predefined when displayed (i.e., they happen to look good in this
  37.  * order).
  38.  */
  39. static struct lsm_entry field_names = {
  40.     "Title",
  41.     "Version",
  42.     "Entered-date",
  43.     "Description",
  44.     "Keywords",
  45.     "Author",
  46.     "Maintained-by",
  47.     "Primary-site",
  48.     "Alternate-site",
  49.     "Original-site",
  50.     "Platforms",
  51.     "Copying-policy",
  52. };
  53.  
  54.  
  55.  
  56. /*
  57.  * Find the index for a field name.  If the field name isn't there already,
  58.  * add it to the table.  Return the index; kill program on error (name not
  59.  * in table, can't be added).
  60.  */
  61. int lsm_field_to_index(const char *s) {
  62.     int i;
  63.  
  64.     for (i = 0; i < MAX_FIELDS; ++i)
  65.         if (strcmp(s, field_names.fields[i]) == 0)
  66.             return i;
  67.     for (i = 0; i < MAX_FIELDS && field_names.fields[i] != NULL; ++i)
  68.         continue;
  69.     if (i == MAX_FIELDS)
  70.         errormsg(1, 0, "Too many fields (max is %d)", MAX_FIELDS);
  71.     field_names.fields[i] = xstrdup(s);
  72.     return i;
  73. }
  74.  
  75.  
  76. /*
  77.  * Return the field name corresponding to a given index.
  78.  */
  79. const char *lsm_index_to_field(int i) {
  80.     assert(i >= 0);
  81.     assert(i < MAX_FIELDS);
  82.     return field_names.fields[i];
  83. }
  84.  
  85.  
  86.  
  87. /*
  88.  * Is this line the "End" line?
  89.  */
  90. static int is_end_line(const char *line) {
  91.     if (strncmp(line, "End", 3) != 0)
  92.         return 0;
  93.     for (line += 3; isspace(*line); ++line)
  94.         continue;
  95.     return *line == '\0';
  96. }
  97.  
  98.  
  99.  
  100. /*
  101.  * Read one entry from the file f.  Skip all leading garbage, but don't
  102.  * read past the `End' line.  Return 0 for no entry found, >0 for ok,
  103.  * -1 for error.
  104.  */
  105. int lsm_read_one_entry(FILE *f, long *lineno, struct lsm_entry *e) {
  106.     int i, prev;
  107.     char *p, *q;
  108.  
  109.     assert(f != NULL);
  110.     assert(lineno != NULL);
  111.     assert(*lineno >= 1);
  112.     assert(e != NULL);
  113.  
  114.     /* Initialize the entry */
  115.     for (i = 0; i < MAX_FIELDS; ++i)
  116.         e->fields[i] = NULL;
  117.  
  118.     /* Find "Begin3" that starts the entry. */
  119.     for (;;) {
  120.         p = getaline(f);
  121.         if (p == NULL) {
  122.             if (ferror(f) || !feof(f))
  123.                 return -1;
  124.             return 0;
  125.         }
  126.         ++(*lineno);
  127.         if (strcmp(p, "Begin3") == 0) {
  128.             free(p);
  129.             break;    /* memory leak? */
  130.         }
  131.         free(p);
  132.     }
  133.  
  134.     /* Read until "End" that ends the entry. */
  135.     prev = 0;
  136.     for (;;) {
  137.         p = getaline(f);
  138.         if (p == NULL) {
  139.             if (ferror(f) || !feof(f))
  140.                 return -1;
  141.             errormsg(0, 0, "Missing `End' line\n");
  142.             return -1;
  143.         }
  144.         ++(*lineno);
  145.  
  146.         if (is_end_line(p)) {
  147.             for (i = 0; i < MAX_FIELDS; ++i) {
  148.                 if (field_is_empty(e->fields[i])) {
  149.                     free(e->fields[i]);
  150.                     e->fields[i] = NULL;
  151.                 }
  152.             }
  153.             return 1;
  154.         }
  155.  
  156.         if (isspace(*p) || *p == '\0') {
  157.             char *t;
  158.             q = e->fields[prev];
  159.             if (q == NULL) {
  160.                 errormsg(0, 0, "Line %ld has no keyword",
  161.                     *lineno);
  162.                 return -1;
  163.             }
  164.             t = p;
  165.             e->fields[prev] = stracat(q, t, "\n", (char*)NULL);
  166.             if (e->fields[prev] == NULL) {
  167.                 errormsg(0, -1,
  168.                     "Out of memory? stracat failed");
  169.                 return -1;
  170.             }
  171.             free(p);
  172.             free(q);
  173.         } else {
  174.             q = strchr(p, ':');
  175.             if (q == NULL) {
  176.                 errormsg(0, 0,
  177.                     "No keyword on line %ld", *lineno);
  178.                 return -1;
  179.             }
  180.             *q++ = '\0';
  181.  
  182.             i = lsm_field_to_index(p);
  183.             if (e->fields[i] != NULL) {
  184.                 errormsg(0, 0,
  185.                     "Same keyword many times on line %ld",
  186.                     *lineno);
  187.                 return -1;
  188.             }
  189.  
  190.             e->fields[i] = stracat(q, "\n", (char *)NULL);
  191.             if (e->fields[i] == NULL) {
  192.                 errormsg(0, -1, "out of mem? stracat failed");
  193.                 return -1;
  194.             }
  195.             free(p);
  196.             prev = i;
  197.         }
  198.     }
  199. }
  200.  
  201.  
  202. /*
  203.  * Read a whole database, i.e., a file with many entries in it.  Return
  204.  * -1 for error (even if any entries were * read), 0 for ok.  The entries
  205.  * are appended to those that already exist in the database.  Nothing
  206.  * is sorted.
  207.  *
  208.  * Support for reading old format entries may be added in the future.
  209.  */
  210. int lsm_read_database(FILE *f, struct lsm_database *db) {
  211.     struct lsm_entry *p;
  212.     size_t newsize;
  213.     long lineno;
  214.     int ret;
  215.     const size_t inc = 1024;
  216.  
  217.     assert(f != NULL);
  218.     assert(db != NULL);
  219.  
  220.     lineno = 1;
  221.  
  222.     for (;;) {
  223.         assert(db->nentries <= db->nalloc);
  224.         if (db->nentries == db->nalloc) {
  225.             newsize = db->nalloc + inc;
  226.             p = realloc(db->entries, newsize * sizeof(*p));
  227.             if (p == NULL) {
  228.                 errormsg(0, -1, "realloc failed");
  229.                 return -1;
  230.             }
  231.             db->entries = p;
  232.             db->nalloc = newsize;
  233.         }
  234.  
  235.         ret = lsm_read_one_entry(f, &lineno, db->entries+db->nentries);
  236.         if (ret <= 0)
  237.             return ret;
  238.         ++db->nentries;
  239.     }
  240. }
  241.  
  242.  
  243. /*
  244.  * Write one entry (in new format) to a file.
  245.  */
  246. int lsm_write_one_entry(FILE *f, const struct lsm_entry *e) {
  247.     int i;
  248.  
  249.     fprintf(f, "Begin3\n");
  250.     for (i = 0; i < MAX_FIELDS; ++i)
  251.         if (e->fields[i] != NULL)
  252.             fprintf(f, "%s:%s",
  253.                 field_names.fields[i], e->fields[i]);
  254.     fprintf(f, "End\n\n");
  255.     if (ferror(f))
  256.         return -1;
  257.     return 0;
  258. }
  259.  
  260.  
  261. /*
  262.  * Write a whole database to the file.
  263.  */
  264. int lsm_write_database(FILE *f, struct lsm_database *db) {
  265.     size_t i;
  266.  
  267.     for (i = 0; i < db->nentries; ++i)
  268.         if (lsm_write_one_entry(f, &db->entries[i]) == -1)
  269.             return -1;
  270.     if (fflush(f) == EOF || ferror(f))
  271.         return -1;
  272.     return 0;
  273. }
  274.  
  275.  
  276.  
  277.  
  278.  
  279. /*
  280.  * Check that all mandatory fields are present, and that fields with
  281.  * special syntax follow it.  Return 0 for ok, -1 for error; print
  282.  * appropriate error messages.
  283.  */
  284. int lsm_check_entry(struct lsm_entry *e, const char *msgprefix) {
  285.     static const char *mandatory[] = {
  286.         "Title",
  287.         "Version",
  288.         "Description",
  289.         "Primary-site",
  290.     };
  291.     static const char *emails[] = {
  292.         "Author",
  293.         "Maintained-by",
  294.     };
  295.     static const char *dates[] = {
  296.         "Entered-date",
  297.         "Checked-date",
  298.     };
  299.     static const char *sites[] = {
  300.         "Primary-site",
  301.         "Alternate-site",
  302.         "Original-site",
  303.     };
  304.     static const char *usual[] = {
  305.         "Title",
  306.         "Version",
  307.         "Description",
  308.         "Keywords",
  309.         "Author",
  310.         "Maintained-by",
  311.         "Primary-site",
  312.         "Alternate-site",
  313.         "Original-site",
  314.         "Platforms",
  315.         "Copying-policy",
  316.         "Entered-date",
  317.         "Checked-status",
  318.         "Checked-date",
  319.     };
  320.     struct lsm_entry ee;
  321.     int i, j, ret;
  322.  
  323.     assert(e != NULL);
  324.     assert(msgprefix != NULL);
  325.  
  326.     ret = 0;
  327.  
  328.     /* Check that all mandatory fields are present. */
  329.     for (i = 0; i < sizeof(mandatory)/sizeof(char *); ++i) {
  330.         j = lsm_field_to_index(mandatory[i]);
  331.         if (e->fields[j] == NULL) {
  332.             errormsg(0, 0, "%s%sMandatory field `%s' is missing.",
  333.                 msgprefix, *msgprefix ? ": " : "",
  334.                 mandatory[i]);
  335.             ret = -1;
  336.         }
  337.     }
  338.  
  339.     /* Check for extra fields. */
  340.     for (i = 0; i < MAX_FIELDS; ++i)
  341.         ee.fields[i] = NULL;
  342.     for (i = 0; i < sizeof(usual)/sizeof(char *); ++i) {
  343.         j = lsm_field_to_index(usual[i]);
  344.         ee.fields[j] = "";
  345.     }
  346.     for (i = 0; i < MAX_FIELDS; ++i) {
  347.         if (ee.fields[i] == NULL && e->fields[i] != NULL) {
  348.             errormsg(0, 0, "%s%sExtra field `%s'.",
  349.                 msgprefix, *msgprefix ? ": " : "",
  350.                 lsm_index_to_field(i));
  351.             ret = -1;
  352.         }
  353.     }
  354.  
  355.     /* Check all e-mail fields. */
  356.     for (i = 0; i < sizeof(emails)/sizeof(char *); ++i) {
  357.         j = lsm_field_to_index(emails[i]);
  358.         if (j >= 0 && !valid_email(e->fields[j])) {
  359.             errormsg(0, 0,
  360.                 "%s%sBad email address `%.*s' in field `%s'?",
  361.                 msgprefix, *msgprefix ? ": " : "",
  362.                 (int)strcspn(e->fields[j], "\n"),
  363.                 e->fields[j], emails[i]);
  364.             ret = -1;
  365.         }
  366.     }
  367.  
  368.     /* Check dates */
  369.     for (i = 0; i < sizeof(dates)/sizeof(char *); ++i) {
  370.         j = lsm_field_to_index(dates[i]);
  371.         if (j >= 0 && !valid_date(e->fields[j])) {
  372.             errormsg(0, 0, "%s%sBad date `%.*s' in field `%s'",
  373.                 msgprefix, *msgprefix ? ": " : "",
  374.                 (int)strcspn(e->fields[j], "\n"),
  375.                 e->fields[j], dates[i]);
  376.             ret = -1;
  377.         }
  378.     }
  379.  
  380.     /* Check the sites */
  381.     for (i = 0; i < sizeof(sites)/sizeof(char *); ++i) {
  382.         j = lsm_field_to_index(sites[i]);
  383.         if (j >= 0 && !valid_site(e->fields[j])) {
  384.             errormsg(0, 0, "%s%sBad site spec `%.*s' in field `%s'",
  385.                 msgprefix, *msgprefix ? ": " : "",
  386.                 (int)strcspn(e->fields[j], "\n"),
  387.                 e->fields[j], sites[i]);
  388.             ret = -1;
  389.         }
  390.     }
  391.  
  392.     return ret;
  393. }
  394.  
  395.  
  396.  
  397. /*
  398.  * Check whether an e-mail address is valid.  A very simple, but hopefully
  399.  * effective enough method is making sure there is an `@' character in it.
  400.  * Full checking would be much more troublesome.
  401.  */
  402. static int valid_email(const char *s) {
  403. #if 0
  404.     return s == NULL || strchr(s, '@') != NULL;
  405. #else
  406.     return 1;
  407. #endif
  408. }
  409.  
  410.  
  411. /*
  412.  * Check that a date is valid, i.e., in ddMMMyy format (where MMM is a
  413.  * three letter, upper case abbreviation of * a month).
  414.  */
  415. static int valid_date(const char *s) {
  416.     int dd, mm, yy, leap;
  417.     static const char *months[12] = {
  418.         "JAN", "FEB", "MAR", "APR", "MAY", "JUN",
  419.         "JUL", "AUG", "SEP", "OCT", "NOV", "DEC",
  420.     };
  421.     static const int monlen[2][12] = {
  422.         { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  423.         { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
  424.     };
  425.  
  426.     if (s == NULL)
  427.         return 1;
  428.  
  429.     while (isspace(*s))
  430.         ++s;
  431.  
  432.     if (!isdigit(s[0]) || !isdigit(s[1]))
  433.         return 0;
  434.     if (!isdigit(s[5]) || !isdigit(s[6]))
  435.         return 0;
  436.     dd = 10*(s[0]-'0') + (s[1]-'0');
  437.     yy = 10*(s[5]-'0') + (s[6]-'0') + 1900;
  438.  
  439.     for (mm = 0; mm < 12; ++mm)
  440.         if (strncasecmp(s+2, months[mm], 3) == 0)
  441.             break;
  442.     if (mm == 12)
  443.         return 0;
  444.  
  445.     leap = (yy % 4) == 0 && ((yy % 100) != 0 || (yy % 400) == 0);
  446.     if (mm < 0 || mm > 12 || dd < 0 || dd > monlen[leap][mm])
  447.         return 0;
  448.  
  449.     return 1;
  450. }
  451.  
  452.  
  453.  
  454. /*
  455.  * Check that a site specification is valid.
  456.  */
  457. static int valid_site(const char *s) {
  458.     return 1;  /* let's take it easy for now. */
  459. }
  460.